use std::fmt::Debug;

use axum::async_trait;

use tower_sessions::{
    session::{Id, Record},
    session_store, SessionStore,
};

use crate::AppState;

/// 使用AppState的cache作为session的存储层
#[derive(Clone)]
pub struct StateStore(AppState);

impl StateStore {
    /// Create a new AppState store with the provided state.
    pub fn new(state: AppState) -> Self {
        Self(state)
    }
}

impl Debug for StateStore {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_tuple("StateStore").finish()
    }
}

#[async_trait]
impl SessionStore for StateStore {
    async fn create(&self, record: &mut Record) -> session_store::Result<()> {
        loop {
            if self.0.cache().get(&record.id.to_string()).await.is_some() {
                record.id = Id::default();
                continue;
            }
            break;
        }
        let duration = record.expiry_date - time::OffsetDateTime::now_utc();
        let duration = (duration.whole_milliseconds() + 1000).max(0) as u64;
        self.0
            .cache()
            .set_ex(
                &record.id.to_string(),
                serde_json::to_string(record).unwrap(),
                duration,
            )
            .await;
        Ok(())
    }

    async fn save(&self, record: &Record) -> session_store::Result<()> {
        if self.0.cache().get(&record.id.to_string()).await.is_some() {
            let duration = record.expiry_date - time::OffsetDateTime::now_utc();
            let duration = (duration.whole_milliseconds() + 1000).max(0) as u64;
            // 当key存在时保存
            self.0
                .cache()
                .set_ex(
                    &record.id.to_string(),
                    serde_json::to_string(record).unwrap(),
                    duration,
                )
                .await;
        }
        Ok(())
    }

    async fn load(&self, session_id: &Id) -> session_store::Result<Option<Record>> {
        let data = self.0.cache().get(&session_id.to_string()).await;

        if let Some(data) = data {
            Ok(Some(serde_json::from_str(&data).unwrap()))
        } else {
            Ok(None)
        }
    }

    async fn delete(&self, session_id: &Id) -> session_store::Result<()> {
        self.0.cache().del(&session_id.to_string()).await;
        Ok(())
    }
}
